package graphics;


import util.InvalidStateException;
import javax.swing.UIDefaults;
import processing.core.*;
//import sun.security.util.Debug;
import util.Movement;

/**
 *
 * @author Emanuel Quintero
 */
public class Puzzle8Graph extends PApplet {

    private final int[] DEFAULT_INI_STATE = {0, 1, 2, 3, 4, 5, 6, 7, 8};
    //Para acceder a el ancho y la altura SOLO utilizar width y height
    private int w, h;
    //Corner: Esquina superior izquierda del primer tile
    //        Contiene dos elementos [x,y]
    private int[] corner;
    //Espaciado entre tiles
    private int spacing;
    //Tamano del tile
    private int tileSize;
    //Velocidades de movimiento [lento,medio,rapido]
    private double[] speeds;
    //Velocidad seleccionada
    private int speed;
    //Arreglo de posiciones iniciales
    private int[] iniState;
    //Posicion del cero en el arreglo
    private int zeroPos;
    
    private boolean movable = false;

    Tile[] tiles;

    public Puzzle8Graph(int size) {
        this.w = size;
        this.h = size;
        this.iniState = DEFAULT_INI_STATE;
        tiles = new Tile[9];
        tiles[0] = new Tile(this, 0, 0, 0, 100);
        corner = new int[2];
        speeds = new double[3];
        speed = 0;
    }

    public Puzzle8Graph(int size, int[] iniState){
        this(size);
        if (iniState.length != 9) {
            throw new InvalidStateException();  
        } else {
            this.iniState = iniState;
        }
    }

    @Override
    public void setup() {
        //Ser inicializa el tamaño del PApplet
        tiles[0].display();
        int titleBarHt = getBounds().y;
        size(w, h - titleBarHt);

        //Se inicializa en tamaño de los tiles
        //Entre los 3 tiles, ocuparan el 80% del espacio
        tileSize = (int) (0.8 * height / 3);
        //Se inicializa el espacio entre tiles, el espacio restante
        spacing = (int) ((height - tileSize * 3) / 6);
        //Se inicializa la esquina superior izquierda para
        //comenzar a dibujar a partir de alli
        corner[0] = (int) ((width - (tileSize * 3 + spacing * 2)) / 2);
        corner[1] = spacing * 2;

        //Se inicializan los tiles de acuerdo al orden de iniState
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                tiles[i * 3 + j] = new Tile(this, iniState[i * 3 + j], corner[0] + (tileSize + spacing) * j, corner[1] + (tileSize + spacing) * i, tileSize);
                if (iniState[i * 3 + j] == 0) {
                    zeroPos = i * 3 + j;
                }
            }
        }

        speeds[0] = 1;
        speeds[1] = tileSize/4;
        speeds[2] = tileSize/2;

        background(255);
        frameRate(30);
        smooth();
        noLoop();
    }

    @Override
    public void draw() {
        
        background(230);
        for (int i = 0; i < 9; i++) {
            if (i != zeroPos) {
                tiles[i].display();
            }
        }
    }

    @Override
    public void keyPressed() {

        if (key == CODED && movable) {
            //Si quiero mover una ficha hacia un sentido,
            //el cero se desplaza en sentido contrario
            if (keyCode == UP) {
                System.out.println("KeyPressed:"+ "UP");
                move(Movement.DOWN);
            } else if (keyCode == RIGHT) {
                System.out.println("KeyPressed:"+ "RT");
                move(Movement.LEFT);
            } else if (keyCode == DOWN) {
                System.out.println("KeyPressed:"+ "DN");
                move(Movement.UP);
            } else if (keyCode == LEFT) {
                System.out.println("KeyPressed:"+ "LT");
                move(Movement.RIGHT);
            }
        }
    }

    public void move(Movement mov) {
        Tile zeroTile;
        int deltaPos;
        int movedTile;
        double goalState;

        switch (mov) {
            case UP:
                goalState = tiles[zeroPos].getYPos();
                deltaPos = zeroPos > 2 ? -3 : 0;
                break;
            case RIGHT:
                goalState = tiles[zeroPos].getXPos();
                deltaPos = (zeroPos % 3) < 2 ? 1 : 0;
                break;
            case DOWN:
                goalState = tiles[zeroPos].getYPos();
                deltaPos = zeroPos < 6 ? 3 : 0;
                break;
            case LEFT:
                goalState = tiles[zeroPos].getXPos();
                deltaPos = (zeroPos % 3) > 0 ? -1 : 0;
                break;
            default:
                return;
        }
        
        if(deltaPos==0)
            return;

        //Swap de fichas
        zeroTile = tiles[zeroPos];
        tiles[zeroPos] = tiles[zeroPos + deltaPos];
        //Se guarda la posicion de la ficha intercambiada
        movedTile = zeroPos;
        //Se actualiza la posicion en el arreglo del cero
        zeroPos += deltaPos;
        tiles[zeroPos] = zeroTile;
        //Se actualiza la posicion en el tablero del cero
        tiles[zeroPos].setPos(tiles[movedTile].getXPos(), tiles[movedTile].getYPos());
        moveTile(movedTile, mov, goalState);
    }

    private void moveTile(int tilePos, Movement mov, double goalState) {
        Tile movingTile = tiles[tilePos];
        //Debug.println("GoalState", "" + goalState);
        while (!movingTile.movement(mov, speeds[speed], goalState)) {
            redraw();
        }
    }
}
